﻿using Arborium;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ArboriumExplorer
{
    /// <summary>
    /// Interaction logic for DataEditor.xaml
    /// </summary>
    public partial class DataEditor : UserControl
    {
        public sealed class Constants
        {
            public const string AsciiString = "ASCII string";
            public const string Bool = "Bool";
            public const string Byte = "Byte";
            public const string Char = "Character";
            public const string DateTime = "DateTime or TimeSpan";
            public const string Decimal = "Decimal";
            public const string Double = "Double";
            public const string Float = "Float";
            public const string Guid = "Guid";
            public const string Int16 = "Short integer";
            public const string Int32 = "Integer";
            public const string Int64 = "Long integer";
            public const string UInt16 = "Unsigned short";
            public const string UInt32 = "Unsigned int";
            public const string UInt64 = "Unsigned long";
            public const string Utf8String = "UTF-8 string";
            public const string Utf16leString = "UTF-16LE string";
            public const string Utf32leString = "UTF-32LE string";
            public const string VarInt = "Variable length integer";
            public const string VarUInt = "Variable length unsigned integer";
        }

        private Stream _data;

        public Stream Data
        {
            get 
            {
                return _data; 
            }
            set
            {
                _data = value;
                PresentCheckBox.IsChecked = Data.Length != 0;
                PresentCheckBox_Checked(PresentCheckBox, null);
                UpdateButton.IsEnabled = false;
            }
        }

        public string Type 
        {
            get { return DataTypeLabel.Content.ToString(); }
            set { DataTypeLabel.Content = value; }
        }

        private List<string> types = new List<string>()
            {
                Constants.AsciiString,
                Constants.Bool,
                Constants.Byte,
                Constants.Char,
                Constants.DateTime,
                Constants.Decimal,
                Constants.Double,
                Constants.Float,
                Constants.Guid,
                Constants.Int16,
                Constants.Int32,
                Constants.Int64,
                Constants.UInt16,
                Constants.UInt32,
                Constants.UInt64,
                Constants.Utf8String,
                Constants.Utf16leString,
                Constants.Utf32leString,
                Constants.VarInt,
                Constants.VarUInt
            };

        public DataEditor()
        {
            InitializeComponent();

            TypeCombo.ItemsSource = types;
            UpdateButton.IsEnabled = false;
        }

        private void PresentCheckBox_Checked(object sender, RoutedEventArgs e)
        {
            bool enabled = ((CheckBox)sender).IsChecked.Value;

            FromTextRadioButton.IsChecked = true;
            FromFileRadioButton.IsChecked = false;

            FromFileRadioButton.IsEnabled = enabled;
            FromTextRadioButton.IsEnabled = enabled;
            TypeCombo.IsEnabled = enabled;
            ValueTextBox.IsEnabled = enabled;
            FileName.IsEnabled = enabled;
            BrowseFileButton.IsEnabled = enabled;
            ExportFileButton.IsEnabled = enabled;

            if (enabled)
            {
                FromTextRadioButton_Checked(FromTextRadioButton, null);
            }

            UpdateButton.IsEnabled = true;
        }

        private void FromTextRadioButton_Checked(object sender, RoutedEventArgs e)
        {
            bool enabled = ((RadioButton)sender).IsChecked.Value;

            TypeCombo.IsEnabled = enabled;
            ValueTextBox.IsEnabled = enabled;
            FileName.IsEnabled = !enabled;
            BrowseFileButton.IsEnabled = !enabled;
            ExportFileButton.IsEnabled = !enabled;
        }

        private void TypeCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string selectedType = ((ComboBox)sender).SelectedItem as string;

            ValueTextBox.Background = Brushes.White;

            try
            {
                switch (selectedType)
                {
                    case Constants.AsciiString:
                        ValueTextBox.Text = AData.AsAsciiString(_data);
                        break;
                    case Constants.Bool:
                        ValueTextBox.Text = AData.AsBool(_data).ToString();
                        break;
                    case Constants.Byte:
                        ValueTextBox.Text = AData.AsByte(_data).ToString();
                        break;
                    case Constants.Char:
                        ValueTextBox.Text = AData.AsChar(_data).ToString();
                        break;
                    case Constants.DateTime:
                        ValueTextBox.Text = AData.AsDateTime(_data).ToString();
                        break;
                    case Constants.Decimal:
                        ValueTextBox.Text = AData.AsDecimal(_data).ToString();
                        break;
                    case Constants.Double:
                        ValueTextBox.Text = AData.AsDouble(_data).ToString();
                        break;
                    case Constants.Float:
                        ValueTextBox.Text = AData.AsFloat(_data).ToString();
                        break;
                    case Constants.Guid:
                        ValueTextBox.Text = AData.AsGuid(_data).ToString();
                        break;
                    case Constants.Int16:
                        ValueTextBox.Text = AData.AsShort(_data).ToString();
                        break;
                    case Constants.Int32:
                        ValueTextBox.Text = AData.AsInt(_data).ToString();
                        break;
                    case Constants.Int64:
                        ValueTextBox.Text = AData.AsLong(_data).ToString();
                        break;
                    case Constants.UInt16:
                        ValueTextBox.Text = AData.AsUShort(_data).ToString();
                        break;
                    case Constants.UInt32:
                        ValueTextBox.Text = AData.AsUInt(_data).ToString();
                        break;
                    case Constants.UInt64:
                        ValueTextBox.Text = AData.AsULong(_data).ToString();
                        break;
                    case Constants.Utf8String:
                        ValueTextBox.Text = AData.AsUtf8String(_data);
                        break;
                    case Constants.Utf16leString:
                        ValueTextBox.Text = AData.AsUtf16String(_data);
                        break;
                    case Constants.Utf32leString:
                        ValueTextBox.Text = AData.AsUtf32String(_data);
                        break;
                    case Constants.VarInt:
                        ValueTextBox.Text = AData.AsVarInt(_data).ToString();
                        break;
                    case Constants.VarUInt:
                        ValueTextBox.Text = AData.AsVarUInt(_data).ToString();
                        break;
                    default:
                        throw new Exception();
                }
            }
            catch (Exception)
            {
                ValueTextBox.Background = Brushes.Pink;
            }
        }

        private void ValueTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            ValueTextBox.Background = Brushes.White;
            UpdateButton.IsEnabled = true;
        }

        private void UpdateButton_Click(object sender, RoutedEventArgs e)
        {
            string selectedType = TypeCombo.SelectedItem as string;

            ValueTextBox.Background = Brushes.White;

            if (PresentCheckBox.IsChecked.Value)
            {
                if (FromTextRadioButton.IsChecked.Value)
                {
                    using (MemoryStream backup = new MemoryStream())
                    {
                        _data.CopyTo(backup);
                        backup.Position = 0;
                        _data.SetLength(0);

                        try
                        {
                            switch (selectedType)
                            {
                                case Constants.AsciiString:
                                    AData.FromAsciiString(ValueTextBox.Text).CopyTo(_data);
                                    break;
                                case Constants.Bool:
                                    AData.FromBool(bool.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Byte:
                                    AData.FromByte(byte.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Char:
                                    AData.FromChar(ValueTextBox.Text[0]).CopyTo(_data);
                                    break;
                                case Constants.DateTime:
                                    AData.FromDateTime(DateTime.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Decimal:
                                    AData.FromDecimal(decimal.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Double:
                                    AData.FromDouble(double.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Float:
                                    AData.FromFloat(float.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Guid:
                                    AData.FromGuid(new Guid(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Int16:
                                    AData.FromShort(short.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Int32:
                                    AData.FromInt(int.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Int64:
                                    AData.FromLong(long.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.UInt16:
                                    AData.FromUShort(ushort.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.UInt32:
                                    AData.FromUInt(uint.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.UInt64:
                                    AData.FromULong(ulong.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.Utf8String:
                                    AData.FromUtf8String(ValueTextBox.Text).CopyTo(_data);
                                    break;
                                case Constants.Utf16leString:
                                    AData.FromUtf16String(ValueTextBox.Text).CopyTo(_data);
                                    break;
                                case Constants.Utf32leString:
                                    AData.FromUtf32String(ValueTextBox.Text).CopyTo(_data);
                                    break;
                                case Constants.VarInt:
                                    AData.FromVarInt(long.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                case Constants.VarUInt:
                                    AData.FromVarUInt(ulong.Parse(ValueTextBox.Text)).CopyTo(_data);
                                    break;
                                default:
                                    throw new Exception();
                            }

                            UpdateButton.IsEnabled = false;
                        }
                        catch (Exception)
                        {
                            backup.CopyTo(_data);
                            ValueTextBox.Background = Brushes.Pink;
                        }
                    }
                }
                else
                {
                    try
                    {
                        using (FileStream fs = new FileStream(FileName.Text, FileMode.Open))
                        {
                            fs.CopyTo(_data);
                            UpdateButton.IsEnabled = false;
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(String.Format("Could not open the file \"{0}\"!\n{1}", FileName.Text, ex.Message),
                            "Error", MessageBoxButton.OK, MessageBoxImage.Hand);
                    }
                }
            }
            else
            {
                _data.SetLength(0);
            }
        }

        private void BrowseFileButton_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openDialog = new OpenFileDialog
            {
                FileName = "",
                Filter = "All files|*.*"
            };

            if (openDialog.ShowDialog() == true)
            {
                string filename = openDialog.FileName;
                FileName.Text = filename;
                UpdateButton.IsEnabled = true;
            }
        }

        private void ExportFileButton_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                using (FileStream fs = new FileStream(FileName.Text, FileMode.Create))
                {
                    _data.Position = 0;
                    _data.CopyTo(fs);
                }

                MessageBox.Show(String.Format("Successfully exported to the file \"{0}\".\n", FileName.Text),
                    "Success", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show(String.Format("Could not export the file \"{0}\"!\n{1}", FileName.Text, ex.Message),
                    "Error", MessageBoxButton.OK, MessageBoxImage.Hand);
            }
        }
    }
}
